/*
 * Copyright (c) 2020 Fraunhofer FOKUS and others. All rights reserved.
 *
 * See the NOTICE file(s) distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contact: mosaic@fokus.fraunhofer.de
 */
package org.eclipse.mosaic.app.enterprise.mec;

import com.google.common.collect.Lists;
import org.eclipse.mosaic.fed.application.app.AbstractApplication;
import org.eclipse.mosaic.fed.application.app.api.MqttApplication;
import org.eclipse.mosaic.fed.application.app.api.MultiAccessEdgeComputingApplication;
import org.eclipse.mosaic.fed.application.app.api.os.MultiAccessEdgeComputingOperatingSystem;
import org.eclipse.mosaic.fed.mqtt.constants.MqttTopic;
import org.eclipse.mosaic.fed.mqtt.protocol.MecProtocol;
import org.eclipse.mosaic.interactions.mqtt.MqttPublish;
import org.eclipse.mosaic.interactions.out.MecFileOutputUpdates;
import org.eclipse.mosaic.lib.objects.traffic.MecDetectorInfo;
import org.eclipse.mosaic.lib.objects.vehicle.VehicleData;
import org.eclipse.mosaic.lib.util.JsonUtils;
import org.eclipse.mosaic.lib.util.NameGenerator;
import org.eclipse.mosaic.lib.util.scheduling.Event;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class MultiAccessEdgeComputingUnitApp extends AbstractApplication<MultiAccessEdgeComputingOperatingSystem> implements
        MultiAccessEdgeComputingApplication, MqttApplication {

    private static final Logger log = LoggerFactory.getLogger(MultiAccessEdgeComputingUnitApp.class);

    @Override
    public void onStartup() {
        // nop
    }

    @Override
    public void onShutdown() {
        // nop
    }

    @Override
    public void onMecDetectorUpdated(MecDetectorInfo updatedMecDetector) {
        String mecName = getOs().getExtName();
        long time = System.currentTimeMillis();

        String topic = MqttTopic.MEC.getTopic(mecName);

        MecProtocol.MecHeader header = new MecProtocol.MecHeader.Builder().setTimestamp(time).defaultValue().build();
        List<MecProtocol.MecObjective> objective = new ArrayList<>();

        for (Map.Entry<Long, VehicleData> entry : updatedMecDetector.getVehicles().entrySet()) {
            Long vehNo = entry.getKey();
            VehicleData vehicleData = entry.getValue();
            String uuid = NameGenerator.getVehicleUuid(vehNo.intValue());

            log.info("Simulate time: {} Vehicle number: {} detected in mec area, data: {}", getOs().getSimulationTime(),
                    vehNo, vehicleData);

            MecProtocol.MecObjective mecObjective = new MecProtocol.MecObjective.Builder()
                    .setAccelVert(vehicleData.getLongitudinalAcceleration())
                    .setHeading(vehicleData.getHeading())
                    .setHeight(vehicleData.getVehicleType().getHeight())
                    .setLatitude(vehicleData.getPosition().getLatitude())
                    .setLongitude(vehicleData.getPosition().getLongitude())
                    .setObjId(vehNo.intValue())
                    .setSpeed(vehicleData.getSpeed())
                    .setLength(vehicleData.getVehicleType().getLength())
                    .setUuid(uuid)
                    .setWidth(vehicleData.getVehicleType().getWidth())
                    .defaultValue()
                    .build();
            objective.add(mecObjective);
        }

        MecProtocol.MecBody body = new MecProtocol.MecBody.Builder()
                .setRcuId(mecName)
                .setObjective(objective)
                .setObjectiveNum(objective.size())
                .setTimestampOfDetIn(time)
                .setTimestampOfDetOut(time)
                .defaultValue().build();

        MecProtocol protocol = new MecProtocol(header, body, time);

        // publish(topic, protocol);
        MecFileOutputUpdates updates = new MecFileOutputUpdates(getOs().getSimulationTime(),
                Lists.newArrayList(JsonUtils.serialize(protocol)));
        getOs().sendInteractionToRti(updates);
    }

    @Override
    public void processEvent(Event event) throws Exception {
        // nop
    }

    @Override
    public void publish(String topic, Object payload) {
        MqttPublish publish = new MqttPublish(getOs().getSimulationTime(), topic, payload, 0, Boolean.FALSE);

        getOs().sendInteractionToRti(publish);
    }
}
